﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.Text;
using System.Windows;
using WPFCABShell.Shell.Interface.Services.Settings;
using WPFCABShell.Shell.Interface.Services.Skinning;
using Microsoft.Practices.CompositeUI;
using Microsoft.Practices.CompositeUI.EventBroker;
using Microsoft.Practices.ObjectBuilder;
using Kent.Boogaart.HelperTrinity;

namespace WPFCABShell.$safeprojectname$.Skinning
{
	public sealed class SkinningService : ISkinningService, IBuilderAware
	{
		private readonly IList<ISkin> _skins;
		private readonly ISettingsService _settingsService;
		private ISkin _currentSkin;
		private string _skinNameToApply;
		private static readonly string _settingsKey = typeof(SkinningService).FullName;
		private const string _skinNameKey = "SkinName";

		public IEnumerable<ISkin> Skins
		{
			get
			{
				return _skins;
			}
		}

		public ISkin CurrentSkin
		{
			get
			{
				return _currentSkin;
			}
			set
			{
				ArgumentHelper.AssertNotNull(value, "value");

				if (_currentSkin != value)
				{
					ISkin oldSkin = _currentSkin;
					_currentSkin = value;
					OnSkinChanged(oldSkin ,_currentSkin);
				}
			}
		}

		[EventPublication(Events.SkinAdded)]
		public event EventHandler<SkinEventArgs> SkinAdded;

		[EventPublication(Events.SkinChanged)]
		public event EventHandler<SkinChangedEventArgs> SkinChanged;

		public SkinningService([ServiceDependency(Required=true)] ISettingsService settingsService)
		{
			_settingsService = settingsService;
			_skins = new List<ISkin>();
		}

		public ISkin Add(string name, Type resourcesType)
		{
			ArgumentHelper.AssertNotNull(name, "name");
			ArgumentHelper.AssertNotNull(resourcesType, "resourcesType");
			ExceptionHelper.ThrowIf(!typeof(ResourceDictionary).IsAssignableFrom(resourcesType), "NotAResourceDictionary");
			ExceptionHelper.ThrowIf(resourcesType.GetConstructor(new Type[] {}) == null, "NoDefaultConstructor");
			
			Skin skin = new Skin(name, resourcesType);
			_skins.Add(skin);
			OnSkinAdded(skin);

			//re-apply skin after application restart
			if (string.Equals(skin.Name, _skinNameToApply, StringComparison.Ordinal))
			{
				CurrentSkin = skin;
				_skinNameToApply = null;
			}

			return skin;
		}

		private void OnSkinAdded(ISkin skin)
		{
			EventHelper.Raise(SkinAdded, this, new SkinEventArgs(skin));
		}

		private void OnSkinChanged(ISkin oldSkin, ISkin newSkin)
		{
			EventHelper.Raise(SkinChanged, this, new SkinChangedEventArgs(oldSkin, newSkin));
		}

		void IBuilderAware.OnBuiltUp(string id)
		{
			ISettings settings = _settingsService.GetSettings(_settingsKey);

			if (settings.Contains(_skinNameKey))
			{
				_skinNameToApply = settings[_skinNameKey] as string;
			}
		}

		void IBuilderAware.OnTearingDown()
		{
			//save the selected skin so we can reapply on next start
			ISettings settings = _settingsService.GetSettings(_settingsKey);
			string skinName = null;

			if (CurrentSkin != null)
			{
				skinName = CurrentSkin.Name;
			}

			settings.Add(_skinNameKey, skinName);
			settings.Save();
		}

		private sealed class Skin : ISkin
		{
			private readonly string _name;
			private readonly Type _resourcesType;

			public string Name
			{
				get
				{
					return _name;
				}
			}

			public Type ResourcesType
			{
				get
				{
					return _resourcesType;
				}
			}

			public Skin(string name, Type resourcesType)
			{
				Debug.Assert(name != null);
				Debug.Assert(resourcesType != null);
				_name = name;
				_resourcesType = resourcesType;
			}
		}
	}
}
